home *** CD-ROM | disk | FTP | other *** search
- /* ****************************************************************
- * D.c
- * ****************************************************************
- * MODULE PURPOSE:
- * This module contains microfacet distribution routines and
- * various support routines. One of support functions includes
- * computing the coefficients corresponding to beta for each
- * function (as described in Section 4.3.1-Improved Specular
- * Reflection). Beta is the angle between H and N where the
- * function is equal to half the value at N = H. Beta is in
- * radians.
- *
- * MODULE CONTENTS:
- * D_phong_init - compute Ns given beta
- * D_phong - Phong cosine power function
- * D_blinn_init - compute Ns given beta
- * D_blinn - Blinn cosine power function
- * D_gaussian_init - compute C1 given beta
- * D_gaussian - Gaussian distribution
- * D_reitz_init - compute c2 given beta
- * D_reitz - Trowbridge-Reitz
- * D_cook_init - compute m given beta
- * D_cook - Cook model
- * D_g - compute apparent roughness
- * D_tau - compute correlation distance
- * D_sigma - compute rms roughness
- * D_m - compute slope
- * D_coherent - coherent roughness attenuation
- * (per Beckmann)
- * D_incoherent - incoherent microfacet attenuation
- * (per Beckmann and Bahar)
- * D_Vxy - Vxy used in D_incoherent()
- *
- * ASSUMPTIONS:
- * > The following are defined in math.h
- * HUGE largest floating pont value
- * M_PI pi
- * M_PI_2 pi/2
- * M_PI_4 pi/4
- * M_SQRT2 root of 2
- *
- * > The direction vectors N, V, and L are assumed to be oriented
- * to the same side of the surface.
- */
- #include <stdio.h>
- #include <math.h>
- #include "geo.h"
- #include "D.h"
-
- /* ****************************************************************
- * double D_phong_init (beta)
- * double beta (in) - angle between N and
- * H where function = .5
- * Returns Ns given beta for the Phong (1975) specular function,
- * per footnote to Eq.(\*(rH)
- */
- double D_phong_init (beta)
- double beta;
- { if (beta <= 0.0) return HUGE;
- if (beta >= M_PI_4) return 0.0;
- return -(log(2.0) / log(cos(2.0 * beta)));
- }
-
- /* ****************************************************************
- * double D_phong (N, L, V, Ns)
- * DIR_VECT *N, *L, *V (in) - N, L, V vectors
- * double Ns (in) - specular exponent
- *
- * Returns the value of the Phong (1975) specular function given
- * surface normal, incident light direction, view direction, and
- * the specular exponent. Refer to Eqs.(\*(rU) and (\*(rI).
- */
- double D_phong (N, L, V, Ns)
- DIR_VECT *N, *L, *V;
- double Ns;
- { double Rv_dot_L;
- if ((Rv_dot_L = geo_dot(geo_rfl(V,N),L)) < 0.0) return 0.0;
- return pow (Rv_dot_L, Ns);
- }
-
- /* ****************************************************************
- * double D_blinn_init (beta)
- * double beta (in) - angle between N and
- * H where function = .5
- * Returns Ns given beta for the cosine power distribution
- * function presented by Blinn (1977). Refer to Eq.(\*(rHa)
- */
- double D_blinn_init (beta)
- double beta;
- { if (beta <= 0.0) return HUGE;
- if (beta >= M_PI_2) return 0.0;
- return -(log(2.0) / log(cos(beta)));
- }
-
- /* ****************************************************************
- * double D_blinn (N, L, V, Ns)
- * DIR_VECT *N, *L, *V (in) - N, L, V vectors
- * double Ns (in) - specular exponent
- *
- * Returns the cosine power distribution function presented by
- * Blinn (1977) given surface normal, incident light direction,
- * view direction, and the specular exponent. Refer to Eq.(\*(e5)
- */
- double D_blinn (N, L, V, Ns)
- DIR_VECT *N, *L, *V;
- double Ns;
- { double N_dot_H;
- if ((N_dot_H = geo_dot(N,geo_H(V,L))) < 0.0) return 0.0;
- return pow (N_dot_H, Ns);
- }
-
- /* ****************************************************************
- * double D_gaussian_init (beta)
- * double beta (in) - angle between N and
- * H where function = .5
- * Returns C1 given beta for the Gaussian distribution presented
- * by Blinn (1977). Refer to Eq.(\*(rHb)
- */
- double D_gaussian_init (beta)
- double beta;
- { if (beta <= 0.0) return HUGE;
- return sqrt(log(2.0)) / beta;
- }
-
- /* ****************************************************************
- * double D_gaussian (N, L, V, C1)
- * DIR_VECT *N, *L, *V (in) - N, L, V vectors
- * double C1 (in) - shape coefficient
- *
- * Returns the Gaussian distribution function presented by
- * Blinn (1977) given surface normal, incident light direction,
- * view direction, and the specular exponent. Refer to Eq.(\*(eA)
- */
- double D_gaussian (N, L, V, C1)
- DIR_VECT *N, *L, *V;
- double C1;
- { double tmp;
- double N_dot_H;
- if ((N_dot_H = geo_dot(N,geo_H(V,L))) < 0.0) return 0.0;
- tmp = acos(N_dot_H) * C1;
- return exp (-(tmp * tmp));
- }
-
- /* ****************************************************************
- * double D_reitz_init (beta)
- * double beta (in) - angle between N and
- * H where function = .5
- * Returns C2 squared given beta for the Trowbridge-Reitz
- * distribution function presented by Blinn (1977). Refer to
- * Eq.(\*(rHc). C2 is squared for computational efficiency later.
- */
- double D_reitz_init (beta)
- double beta;
- { double cos_beta;
- if (beta <= 0.0) return 0.0;
- cos_beta = cos(beta);
- return ((cos_beta * cos_beta) - 1.0) /
- ((cos_beta * cos_beta) - sqrt(2.0));
- }
-
- /* ****************************************************************
- * double D_reitz (N, L, V, C2_2)
- * DIR_VECT *N, *L, *V (in) - N, L, V vectors
- * double C2_2 (in) - C2 squared
- *
- * Returns the Trowbridge-Reitz distribution function presented
- * by Blinn (1977) given surface normal, incident light
- * direction, view direction, and the specular exponent. Refer
- * to Eq.(\*(eB)
- */
- double D_reitz (N, L, V, C2_2)
- DIR_VECT *N, *L, *V;
- double C2_2;
- { double tmp;
- double N_dot_H;
- if ((N_dot_H = geo_dot(N,geo_H(V,L))) < 0.0) return 0.0;
- tmp = C2_2 / ((N_dot_H * N_dot_H * (C2_2 - 1.0)) + 1.0);
- return tmp * tmp;
- }
-
- /* ****************************************************************
- * double D_cook_init (beta)
- * double beta (in) - angle between N and
- * H where function = .5
- * Returns m squared corresponding to beta, refer to Eq.(\*(rT).
- * m is squared for computational efficiency in later use.
- */
- double D_cook_init (beta)
- double beta;
- { double tan_beta;
- if (beta <= 0.0) return 0.0;
- if (beta >= M_PI_2) return HUGE;
- tan_beta = tan(beta);
- return -(tan_beta * tan_beta) /
- log(pow(cos(beta),4.0)/2.0);
- }
-
- /* ****************************************************************
- * double D_cook (N, L, V, m_2)
- * DIR_VECT *N, *L, *V (in) - N, L, V vectors
- * double m_2 (in) - m squared
- *
- * Returns microfacet distribution probability (Cook 1982) derived
- * from (Beckmann 1963). Refer to Eq.(\*(rX).
- */
- double D_cook (N, L, V, m_2)
- DIR_VECT *N, *L, *V;
- double m_2;
- { double tmp;
- double N_dot_H;
- if ((N_dot_H = geo_dot(N,geo_H(V,L))) < 0.0) return 0.0;
- tmp = -(1.0 - (N_dot_H * N_dot_H)) / (N_dot_H * N_dot_H * m_2);
- return exp(tmp)/(4.0 * M_PI * m_2 * pow(N_dot_H,4.0));
- }
-
- /* ****************************************************************
- * double D_g (N, L, V, sigma, lambda)
- * DIR_VECT *N, *L, *V (in) - N, L, V vectors
- * double sigma (in) RMS roughness, nm
- * double lambda (in) wavelength, nm
- *
- * Returns the apparent roughness, g, as given by Beckmann (1963).
- * Refer to Eq.(\*(rY).
- */
- double D_g (N, L, V, sigma, lambda)
- DIR_VECT *N, *L, *V;
- double sigma, lambda;
- { double tmp;
- tmp = geo_dot(N,L) + geo_dot(N,V);
- return (4.0 * M_PI * M_PI * sigma * sigma * tmp * tmp) /
- (lambda * lambda);
- }
-
- /* ****************************************************************
- * double D_tau (sigma, m)
- * double sigma (in) rms roughness (nm)
- * double m (in) rms slope
- *
- * Returns tau (correlation distance) in nm. The relationship of
- * roughness, slope, and correlation distance given by
- * Beckmann (1963) is assumed, refer to Eq.(\*(rD).
- */
- double D_tau (sigma, m)
- double sigma, m;
- { return (2.0 * sigma) / m;
- }
-
- /* ****************************************************************
- * double D_sigma (m, tau)
- * double m (in) rms slope
- * double tau (in) correlation distance (nm)
- *
- * Returns sigma (rms roughness). The relationship of roughness,
- * slope, and correlation distance given by Beckmann (1963) is
- * assumed, refer to Eq.(\*(rD).
- */
- double D_sigma (m, tau)
- double m, tau;
- { return (m * tau)/ 2.0;
- }
-
- /* ****************************************************************
- * double D_m (sigma, tau)
- * double sigma (in) rms roughness (nm)
- * double tau (in) correlation distance (nm)
- *
- * Returns m (rms slope). The relationship of roughness, slope,
- * and correlation distance given by Beckmann (1963) is assumed,
- * refer to Eq.(\*(rD).
- */
- double D_m (sigma, tau)
- double sigma, tau;
- { return (2.0 * sigma) / tau;
- }
-
- /* ****************************************************************
- * double D_coherent (g)
- * double g (in) apparent roughness
- *
- * Returns the coherent roughness attenuation given
- * by Beckmann (1963), refer to Eq.(\*(rZ).
- */
- double D_coherent (g)
- double g;
- { return exp(-g);
- }
-
- /* ****************************************************************
- * double D_incoherent (N, L, V, m, g, lambda, tau)
- * DIR_VECT *N, *L, *V (in) N, L, V vectors
- * double m (in) m (slope)
- * double g (in) apparent roughness
- * double lambda (in) wavelength (nm)
- * double tau (in) correlation distance (nm)
- *
- * Returns the microfacet distribution function given by
- * Beckmann (1963) as interpreted by Koestner (1986), refer to
- * Eq.(\*(rC).
- */
- double D_incoherent (N, L, V, m, g, lambda, tau)
- DIR_VECT *N, *L, *V;
- double m, g, lambda, tau;
- { DIR_VECT *H;
- double D1, D2;
- double denom;
- double N_dot_H, N_dot_H_2;
-
- if (g <= 0.0) return 0.0;
- if ((H = geo_H(V,L)) == NULL) return 0.0;
- if ((N_dot_H = geo_dot(N,H)) <= 0.0) return 0.0;
- N_dot_H_2 = N_dot_H * N_dot_H;
- denom = 4.0 * M_PI * m * m * N_dot_H_2 * N_dot_H_2;
-
- /* if g < 8.0, evaluate the series expansion, Eq.(\*(rCb)
- * terminating when a term falls below 0.00001. For small g
- * the evaluation terminates quickly. For large g the series
- * converges slowly. If g is near 8.0 the first terms of the
- * series will be less that the termination criteria. This
- * requires an additional termination criteria that the values
- * of the terms are decreasing at the time the termination
- * criteria is reached.
- */
- if (g < 8.0) {
- double inc, ct, ct_factorial, g_pow;
- double last_inc, Vxz_t_over_4;
- Vxz_t_over_4 = (tau * tau * D_Vxz(N,L,V,lambda)) / 4.0;
- D1 = 0.0;
- ct = 1.0;
- ct_factorial = 1.0;
- g_pow = g * g;
- inc = 0.0;
- do {
- last_inc = inc;
- inc = (g_pow / (ct * ct_factorial)) *
- exp(-(g + Vxz_t_over_4/ct));
- D1 += inc;
- ct += 1.0;
- ct_factorial *= ct;
- g_pow *= g;
- } while (((inc/denom) > 0.00001) || (inc > last_inc));
- D1 /= denom;
-
- /* if g < 5.0, only the series expansion is used. If
- * 5.0 < g < 8.0, then we are in a transition range
- * for interpolation between the series expansion and
- * the convergence expression.
- */
- if (g < 5.0) return D1;
- }
-
- /* if g > 5.0, evaluate the convergence expression, Eq.(\*(rCc)
- * (this routine would have returned earlier if G < 5.0).
- */
- { double tmp;
- tmp = (N_dot_H_2 - 1.0) / (N_dot_H_2 * m * m);
- D2 = exp(tmp) / denom;
-
- /* if g > 8.0, only the convergence expression is used,
- * otherwise we are in a transition zone between the
- * convergent expression and series expansion.
- */
- if (g > 8.0) return D2;
- }
-
- /* linear interpolation between D1 and D2 for
- * 5.0 < g < 8.0
- */
- return (((8.0 - g) * D1) + ((g - 5.0) * D2)) / 3.0;
- }
-
- /* ****************************************************************
- * double D_Vxz (N, L, V, lambda)
- * DIR_VECT *N, *L, *V (in) N, L, V vectors
- * double lambda (in) wavelength (nm)
- *
- * Returns the value of Vxz per Eq.(\*(rCd).
- */
- double D_Vxz (N, L, V, lambda)
- DIR_VECT *N, *L, *V;
- double lambda;
- { double N_dot_L, N_dot_V, cos_phi;
- DIR_VECT sin_i, sin_r;
- double len_2_i, len_2_r;
-
- /* compute the sine squared of the incident angle
- */
- N_dot_L = geo_dot(N, L);
- len_2_i = 1.0 - (N_dot_L * N_dot_L);
-
- /* compute the sine squared of the reflected angle
- */
- N_dot_V = geo_dot(N, V);
- len_2_r = 1.0 - (N_dot_V * N_dot_V);
-
- /* if the sine of either the incident or the
- * reflected angle is zero, then the middle
- * term is zero.
- */
- if ((len_2_i > 0.0) && (len_2_r > 0.0)) {
- /* the two sine vectors are of length equal to the sine of
- * the angles. The dot product is the cosine times the
- * lengths of the vectors (sines of the angles).
- */
- sin_i.i = L->i - (N_dot_L * N->i);
- sin_i.j = L->j - (N_dot_L * N->j);
- sin_i.k = L->k - (N_dot_L * N->k);
- sin_r.i = V->i - (N_dot_V * N->i);
- sin_r.j = V->j - (N_dot_V * N->j);
- sin_r.k = V->k - (N_dot_V * N->k);
- cos_phi = geo_dot (&sin_i, &sin_r);
- return (4.0 * M_PI * M_PI * (len_2_i +
- (2.0 * cos_phi) + len_2_r)) / (lambda * lambda);
- }
- else {
- return (4.0 * M_PI * M_PI * (len_2_i + len_2_r)) /
- (lambda * lambda);
- }
- }
- /* ************************************************************* */
-